<!DOCTYPE html>
<html lang="zh-Hans">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!--Description-->
    
        <meta name="description" content="Note">
    

    <!--Author-->
    
        <meta name="author" content="兰陵忧患生">
    

    <!-- Title -->
    
    <title>Struts2.x 权威指南 | Lan</title>

    <!-- Bootstrap Core CSS -->
    <link href="//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet"
      href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/styles/default.min.css">
    <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/highlight.min.js"></script>

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/css/style.css">

    <!-- Custom Fonts -->
    <link href="//cdn.bootcss.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
    <link href='//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
    <link href='//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>

    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
</head>

<body>

    <!-- Content -->
    <section class="article-container">
<!-- Back Home -->
<a class="nav-back" href="/">
    <i class="fa fa-puzzle-piece"></i>
</a>

<!-- Page Header -->
<header class="intro-header">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <div class="post-heading">
                    <h1>Struts2.x 权威指南</h1>
                </div>
            </div>
        </div>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">
            <!-- TOC -->
            <div class="toc-article col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Struts-2-核心技术"><span class="toc-text">Struts 2 核心技术</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#Struts-2-的-Action"><span class="toc-text">Struts 2 的 Action</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Action-访问-Servlet-API"><span class="toc-text">Action 访问 Servlet API</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Action-直接访问-Servlet-API"><span class="toc-text">Action 直接访问 Servlet API</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#配置-Action"><span class="toc-text">配置 Action</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#配置-Action-的默认处理类"><span class="toc-text">配置 Action 的默认处理类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#动态方法调用（DMI）"><span class="toc-text">动态方法调用（DMI）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#为-action-元素指定-method-属性"><span class="toc-text">为 action 元素指定 method 属性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用通配符"><span class="toc-text">使用通配符</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#配置默认的-Action"><span class="toc-text">配置默认的 Action</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#管理处理结果"><span class="toc-text">管理处理结果</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#配置结果"><span class="toc-text">配置结果</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Struts2-支持的处理类型"><span class="toc-text">Struts2 支持的处理类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#chain-结果类型"><span class="toc-text">chain 结果类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#plainText-结果类型"><span class="toc-text">plainText 结果类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#redirect-结果类型"><span class="toc-text">redirect 结果类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#redirectAction-结果类型"><span class="toc-text">redirectAction 结果类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#动态结果"><span class="toc-text">动态结果</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#请求参数决定结果"><span class="toc-text">请求参数决定结果</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#全局结果"><span class="toc-text">全局结果</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#属性驱动和模型驱动"><span class="toc-text">属性驱动和模型驱动</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#模型的作用"><span class="toc-text">模型的作用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用模型驱动"><span class="toc-text">使用模型驱动</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Struts2-的异常机制"><span class="toc-text">Struts2 的异常机制</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Struts2-的异常处理哲学"><span class="toc-text">Struts2 的异常处理哲学</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#声明式异常捕捉"><span class="toc-text">声明式异常捕捉</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#输出异常信息"><span class="toc-text">输出异常信息</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#未知处理器"><span class="toc-text">未知处理器</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Struts2-的类型转换"><span class="toc-text">Struts2 的类型转换</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#类型转换的意义"><span class="toc-text">类型转换的意义</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#表现层数据处理"><span class="toc-text">表现层数据处理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#传统的类型转换"><span class="toc-text">传统的类型转换</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Struts2-内建的类型转换器"><span class="toc-text">Struts2 内建的类型转换器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#基于-OGNL-的类型转换"><span class="toc-text">基于 OGNL 的类型转换</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#使用-OGNL-转换成-Map-集合"><span class="toc-text">使用 OGNL 转换成 Map 集合</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用-OGNL-转换成-List-集合"><span class="toc-text">使用 OGNL 转换成 List 集合</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#自定义类型转换器"><span class="toc-text">自定义类型转换器</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#系统需求"><span class="toc-text">系统需求</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#实现类型转换器"><span class="toc-text">实现类型转换器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#局部类型转换器"><span class="toc-text">局部类型转换器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#全局类型转换器"><span class="toc-text">全局类型转换器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#局部类型转换器和全局类型转换器的说明"><span class="toc-text">局部类型转换器和全局类型转换器的说明</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#基于-Struts2-的类型转换器"><span class="toc-text">基于 Struts2 的类型转换器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#数组属性的类型转换器"><span class="toc-text">数组属性的类型转换器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#集合属性的类型转换器"><span class="toc-text">集合属性的类型转换器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#集合类型转换的高级特性"><span class="toc-text">集合类型转换的高级特性</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#指定集合元素的类型"><span class="toc-text">指定集合元素的类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#为-Set-集合的元素指定索引属性"><span class="toc-text">为 Set 集合的元素指定索引属性</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#类型转换中的错误处理"><span class="toc-text">类型转换中的错误处理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#类型转换的错误处理流程"><span class="toc-text">类型转换的错误处理流程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#处理类型转换错误"><span class="toc-text">处理类型转换错误</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#输出类型转换错误"><span class="toc-text">输出类型转换错误</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#处理集合属性的转换错误"><span class="toc-text">处理集合属性的转换错误</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Struts2-的输入校验"><span class="toc-text">Struts2 的输入校验</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#输入校验概述"><span class="toc-text">输入校验概述</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#为什么需要输入校验"><span class="toc-text">为什么需要输入校验</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#客户端校验"><span class="toc-text">客户端校验</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#服务器端校验"><span class="toc-text">服务器端校验</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#类型转换和输入校验"><span class="toc-text">类型转换和输入校验</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#基本输入校验"><span class="toc-text">基本输入校验</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#编写校验规则文件"><span class="toc-text">编写校验规则文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用客户端校验"><span class="toc-text">使用客户端校验</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#校验器的配置风格"><span class="toc-text">校验器的配置风格</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#字段校验器"><span class="toc-text">字段校验器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#非字段校验器配置风格"><span class="toc-text">非字段校验器配置风格</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#短路校验器"><span class="toc-text">短路校验器</span></a></li></ol></li></ol></li></ol>
</div>

            <!-- Post Main Content -->
            <div class="post-content col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <h1 id="Struts-2-核心技术"><a href="#Struts-2-核心技术" class="headerlink" title="Struts 2 核心技术"></a>Struts 2 核心技术</h1><h2 id="Struts-2-的-Action"><a href="#Struts-2-的-Action" class="headerlink" title="Struts 2 的 Action"></a>Struts 2 的 Action</h2><h3 id="Action-访问-Servlet-API"><a href="#Action-访问-Servlet-API" class="headerlink" title="Action 访问 Servlet API"></a>Action 访问 Servlet API</h3><ul>
<li><p>Servlet API HttpServletRequest, HttpSession, ServletContext 对应 JSP 内置对象中的 request, session, appliaction</p>
</li>
<li><p>Struts2 ActionContext 类，访问 Servlet API</p>
</li>
</ul>
<h3 id="Action-直接访问-Servlet-API"><a href="#Action-直接访问-Servlet-API" class="headerlink" title="Action 直接访问 Servlet API"></a>Action 直接访问 Servlet API</h3><ul>
<li><p>ActionContext 来访问 Servlet API ,但这种访问毕竟不能直接获得 Servlet API 实例，提供接口直接访问 Servlet API</p>
<ul>
<li><code>ServletContextAware</code> 直接访问 Web 应用中的 ServeltContext 实例</li>
<li><code>ServletRequestAware</code> HttpServletRequest 实例</li>
<li><code>ServletResponseAware</code> HttpServletResponse 实例</li>
</ul>
</li>
<li><p>提供 ServletActionContext 类</p>
</li>
<li><p>即使我们在 Struts2 的 Action 类中获得了 HttpServletResponse 对象，也不要尝试直接在 Action 中生成对客户端的输出。</p>
</li>
</ul>
<h3 id="配置-Action"><a href="#配置-Action" class="headerlink" title="配置 Action"></a>配置 Action</h3><ul>
<li>class 属性不是必需的，系统默认使用 ActionSupport 类</li>
<li>配置逻辑试图和物理视图之间的映射</li>
</ul>
<h3 id="配置-Action-的默认处理类"><a href="#配置-Action-的默认处理类" class="headerlink" title="配置 Action 的默认处理类"></a>配置 Action 的默认处理类</h3><ul>
<li>配置 Action 的默认处理使用 <code>&lt;default-class-ref class=&quot;...&quot;/&gt;</code></li>
</ul>
<h3 id="动态方法调用（DMI）"><a href="#动态方法调用（DMI）" class="headerlink" title="动态方法调用（DMI）"></a>动态方法调用（DMI）</h3><pre><code class="jsp">// Form 表单
action=&quot;action!methodName.action&quot;
</code></pre>
<ul>
<li>开启系统的动态方法调用</li>
</ul>
<pre><code class="XML">struts.enable.DynamicMethodInvocation=true
</code></pre>
<h3 id="为-action-元素指定-method-属性"><a href="#为-action-元素指定-method-属性" class="headerlink" title="为 action 元素指定 method 属性"></a>为 action 元素指定 method 属性</h3><ul>
<li>将一个 Action 类定义成多个逻辑 Action，Action 类的每个处理方法被映射成一个逻辑 Action</li>
</ul>
<h3 id="使用通配符"><a href="#使用通配符" class="headerlink" title="使用通配符"></a>使用通配符</h3><pre><code class="XML">&lt;action name=&quot;*Pro&quot; class=&quot;org.action.LoginRegisAction&quot; method=&quot;{1}&quot;&gt;
&lt;/action&gt;

&lt;action name=&quot;*Pro&quot; class=&quot;org.action.{1}Action&quot;&gt;
&lt;/action&gt;

&lt;action name=&quot;*&quot;&gt;
    &lt;result&gt;/WEB-INF/content/{1}.jsp&lt;/result&gt;
&lt;/action&gt;

&lt;action name=&quot;*_*&quot; method=&quot;{2}&quot; class=&quot;actions.{1}&quot;&gt;
&lt;/action&gt;
</code></pre>
<ul>
<li><p>Struts2 默认的校验文件命名规则：ActionName-validation.xml</p>
</li>
<li><p>除非请求的 URL 与 Action 的 name 属性绝对相同，否则将按先后顺序决定由哪个 Action 来处理用户请求。</p>
</li>
<li>因此我们应该将名为 * 的 Action 配置在最后。</li>
</ul>
<h3 id="配置默认的-Action"><a href="#配置默认的-Action" class="headerlink" title="配置默认的 Action"></a>配置默认的 Action</h3><ul>
<li>在某些情况下，用户请求非常简单，并不需要进行过多的处理，只是需要进行简单的转发。</li>
<li><p>所有请求都发送给 Struts2 框架，让该框架来处理用户请求，即使只是简单的超链接也如此。</p>
</li>
<li><p>对于只是简单的超链接的请求，可以通过定义 name 为 * 的 Action（该 Action 应该放在最后定义）</p>
</li>
<li>还可以定义一个默认的 Action，当用户请求的 URL 在容器中找不到对应的 Action 时，系统将使用默认的 Action 来处理用户请求。（package 下）</li>
</ul>
<h2 id="管理处理结果"><a href="#管理处理结果" class="headerlink" title="管理处理结果"></a>管理处理结果</h2><h3 id="配置结果"><a href="#配置结果" class="headerlink" title="配置结果"></a>配置结果</h3><pre><code class="XML">// 局部结果
&lt;action ..&gt;
    &lt;result name=&quot;success&quot;&gt;&lt;/&gt;
&lt;/&gt;

// 全局结果
&lt;global-results&gt;
    &lt;result name=&quot;&quot;&gt;&lt;/&gt;
&lt;/&gt;
</code></pre>
<ul>
<li><code>param</code></li>
</ul>
<pre><code class="XML">&lt;result name=&quot;success&quot; type=&quot;dispatcher&quot;&gt;
    &lt;param name=&quot;location&quot;&gt;/thank_you.jsp&lt;/&gt;
    &lt;param name=&quot;parse&quot;&gt;true&lt;/&gt;
&lt;/&gt;

// name 逻辑视图名 默认为 success
// type 结果类型 默认结果类型是 dispatcher 
// 还可以通过配置文件来改变默认的结果类型

// param name
// location 实际视图资源
// parse 指定是否允许在实际视图名字中使用 OGNL 表达式，默认值为 true

&lt;result&gt;/thank_you.jsp&lt;/result&gt;
</code></pre>
<h3 id="Struts2-支持的处理类型"><a href="#Struts2-支持的处理类型" class="headerlink" title="Struts2 支持的处理类型"></a>Struts2 支持的处理类型</h3><ul>
<li>Struts2 的结果类型要求实现 com.opensymphony.xwork.Result</li>
<li><p>需要自己的结果类型，则应该提供一个实现该接口的类，并且在 struts.xml 文件中配置该结果类型</p>
</li>
<li><p>结果类型</p>
<ul>
<li>chain</li>
<li>dispatcher</li>
<li>freemarker</li>
<li>httpheader</li>
<li>redirectAction</li>
<li>stream</li>
<li>velocity</li>
<li>xslt</li>
<li>plainText</li>
</ul>
</li>
</ul>
<h3 id="chain-结果类型"><a href="#chain-结果类型" class="headerlink" title="chain 结果类型"></a>chain 结果类型</h3><ul>
<li>当一个 Action 处理完成之后，系统不想转发到 视图资源，而是希望让另一个 Action 进行下一步处理，此时就需要两个 Action 形成 “链式” 处理。</li>
</ul>
<pre><code class="XML">&lt;action name=&quot;addBook&quot; class=&quot;org.action.AddBookAction&quot;&gt;
    &lt;result type=&quot;chain&quot;&gt;getBooks&lt;/result&gt;
&lt;/action&gt;
</code></pre>
<h3 id="plainText-结果类型"><a href="#plainText-结果类型" class="headerlink" title="plainText 结果类型"></a>plainText 结果类型</h3><ul>
<li>用于显示实际视图资源的源代码</li>
</ul>
<pre><code class="XML">&lt;result type=&quot;plainText&quot;&gt;
    &lt;param name=&quot;location&quot;&gt;/WEB-INF/content/welcome.jsp&lt;/&gt;
    &lt;param name=&quot;charSet&quot;&gt;GBK&lt;/&gt;
&lt;/&gt;
</code></pre>
<h3 id="redirect-结果类型"><a href="#redirect-结果类型" class="headerlink" title="redirect 结果类型"></a>redirect 结果类型</h3><ul>
<li>重定向会失去所有的请求参数，请求属性。也丢失了 Action 的处理结果。</li>
<li>使用 redirect 结果类型的效果是，系统将调用 HttpServletResponse 的 sendRedirect(String) 方法来重定向指定的视图资源，就是重新产生一个请求，因此，所有的请求参数，请求属性，Action 实例和 Action 中封装的属性全部丢失。</li>
</ul>
<pre><code class="XML">&lt;action name=&quot;login&quot; class=&quot;org.action.LoginAction&quot;&gt;
    &lt;result type=&quot;redirect&quot;&gt;/WEB-INF/content/welcome.jsp&lt;/&gt;
&lt;/&gt;
// Action 处理用户请求结束后，系统将重新生成一个请求，直接转入 /WEB-INF/content/welcome.jsp
</code></pre>
<h3 id="redirectAction-结果类型"><a href="#redirectAction-结果类型" class="headerlink" title="redirectAction 结果类型"></a>redirectAction 结果类型</h3><ul>
<li>redirectAction 结果类型与 redirect 类型非常相似，一样是重新生成一个全新的请求。区别在于，redirectAction 使用 ActionMapperFactory 提供的 ActionMapper 来重定向请求。</li>
<li>当需要让一个 Action 处理结束后，直接将请求重定向到另一个 Action 时，应该使用 redirectAction 结果类型。</li>
</ul>
<pre><code class="XML">&lt;package name=&quot;public&quot; extends=&quot;struts-default&quot;&gt;
    &lt;action name=&quot;login&quot; class=&quot;org.action.LoginAction&quot;&gt;
        &lt;result type=&quot;redirectAction&quot;&gt;
            &lt;param name=&quot;actionName&quot;&gt;dashboard&lt;/&gt;
            &lt;param name=&quot;namespace&quot;&gt;/secure&lt;/&gt;
        &lt;/&gt;
    &lt;/action&gt;
&lt;/&gt;
&lt;package name=&quot;secure&quot; extends=&quot;struts-default&quot; namespace=&quot;/secure&quot;&gt;
    &lt;action name=&quot;dashboard&quot; class=&quot;org.action.Dashboard&quot;&gt;
        &lt;result&gt;dashboard.jsp&lt;/&gt;
        &lt;result name=&quot;error&quot; type=&quot;redirectAction&quot;&gt;error&lt;/&gt;
    &lt;/&gt;
    &lt;action name=&quot;error&quot;&gt;
        &lt;result&gt;/WEB-INF/content/error.jsp&lt;/&gt;
    &lt;/&gt;
&lt;/&gt;
</code></pre>
<h3 id="动态结果"><a href="#动态结果" class="headerlink" title="动态结果"></a>动态结果</h3><pre><code class="XML">&lt;action name=&quot;crud_*&quot; class=&quot;lee.CrubAction&quot; method=&quot;{1}&quot;&gt;
    &lt;result name=&quot;input&quot;&gt;/WEB-INF/content/input.jsp&lt;/&gt;
    &lt;result&gt;/WEB-INF/content/{1}.jsp&lt;/&gt;
&lt;/&gt;
</code></pre>
<h3 id="请求参数决定结果"><a href="#请求参数决定结果" class="headerlink" title="请求参数决定结果"></a>请求参数决定结果</h3><ul>
<li><code>${属性名.属性名}</code></li>
</ul>
<pre><code class="XML">&lt;result type=&quot;redirect&quot;&gt;edit.action?skill=${curSkill.name}&lt;/&gt;
&lt;result&gt;/WEB-INF/content/${target}.jsp&lt;/&gt;
</code></pre>
<h3 id="全局结果"><a href="#全局结果" class="headerlink" title="全局结果"></a>全局结果</h3><pre><code class="XML">&lt;global-results&gt;
    &lt;result&gt;/WEB-INF/content/${target}.jsp&lt;/&gt;
&lt;/&gt;
</code></pre>
<ul>
<li>同名的结果，局部结果会覆盖全局结果</li>
<li>只有在 Action 的局部结果里找不到逻辑视图对应的结果时，才会到全局结果里搜索。</li>
</ul>
<h2 id="属性驱动和模型驱动"><a href="#属性驱动和模型驱动" class="headerlink" title="属性驱动和模型驱动"></a>属性驱动和模型驱动</h2><h3 id="模型的作用"><a href="#模型的作用" class="headerlink" title="模型的作用"></a>模型的作用</h3><ul>
<li>模型驱动，模型封装了所有数据，贯穿整个 MVC 流程。模型的作用是，封装用户请求参数和处理结果。</li>
</ul>
<h3 id="使用模型驱动"><a href="#使用模型驱动" class="headerlink" title="使用模型驱动"></a>使用模型驱动</h3><pre><code class="XML">&lt;s:property value=&quot;model.tip&quot;/&gt;

// 或者
&lt;s:property value=&quot;tip&quot;/&gt;
</code></pre>
<h2 id="Struts2-的异常机制"><a href="#Struts2-的异常机制" class="headerlink" title="Struts2 的异常机制"></a>Struts2 的异常机制</h2><ul>
<li>通过声明式的方式管理异常处理</li>
</ul>
<h3 id="Struts2-的异常处理哲学"><a href="#Struts2-的异常处理哲学" class="headerlink" title="Struts2 的异常处理哲学"></a>Struts2 的异常处理哲学</h3><ul>
<li>Action 接口的 execute() 方法抛出全部异常，把异常直接抛出 Struts2 框架处理，Struts2 框架接收到 Action 抛出的异常之后，根据 struts.xml 文件配置的异常映射，转入指定的视图资源。</li>
</ul>
<h3 id="声明式异常捕捉"><a href="#声明式异常捕捉" class="headerlink" title="声明式异常捕捉"></a>声明式异常捕捉</h3><pre><code class="XML">&lt;package name=&quot;default&quot; extends=&quot;struts-default&quot;&gt;
   &lt;global-results&gt;
       &lt;result&gt;/WEB-INF/content/welcome.jsp&lt;/result&gt;
       &lt;result name=&quot;sql&quot;&gt;/WEB-INF/content/exception.jsp&lt;/result&gt;
       &lt;result name=&quot;root&quot;&gt;/WEB-INF/content/exception.jsp&lt;/result&gt;
   &lt;/global-results&gt;

// 全局异常映射
   &lt;global-exception-mappings&gt;
       &lt;exception-mapping exception=&quot;java.sql.SQLException&quot; result=&quot;sql&quot;&gt;&lt;/exception-mapping&gt;
       &lt;exception-mapping exception=&quot;java.lang.Exception&quot; result=&quot;root&quot;&gt;&lt;/exception-mapping&gt;
   &lt;/global-exception-mappings&gt;

   &lt;action name=&quot;login&quot; class=&quot;action.LoginTestAction&quot;&gt;
    // 局部异常映射
       &lt;exception-mapping exception=&quot;exception.MyException&quot; result=&quot;my&quot;/&gt;
       &lt;result name=&quot;my&quot;&gt;/WEB-INF/content/exception.jsp&lt;/result&gt;
       &lt;result name=&quot;error&quot;&gt;/WEB-INF/content/error.jsp&lt;/result&gt;
       &lt;result name=&quot;success&quot;&gt;/WEB-INF/content/welcome.jsp&lt;/result&gt;
   &lt;/action&gt;

   &lt;action name=&quot;*&quot;&gt;
       &lt;result&gt;/WEB-INF/content/{1}.jsp&lt;/result&gt;
   &lt;/action&gt;
&lt;/package&gt;

// exception-mapping 
//  exception 此属性指定该异常映射所设置的异常类型
//  result 系统转入 result 属性所指向的结果
</code></pre>
<ul>
<li>通常，全局异常映射的 result 属性值通常不要使用局部结果。局部则都可以使用。</li>
</ul>
<h3 id="输出异常信息"><a href="#输出异常信息" class="headerlink" title="输出异常信息"></a>输出异常信息</h3><pre><code class="JSP">&lt;s:property value=&quot;exception&quot;/&gt; // 输出异常本身
&lt;s:property value=&quot;exceptionStack&quot;/&gt; // 输出异常堆栈信息
</code></pre>
<h2 id="未知处理器"><a href="#未知处理器" class="headerlink" title="未知处理器"></a>未知处理器</h2><h1 id="Struts2-的类型转换"><a href="#Struts2-的类型转换" class="headerlink" title="Struts2 的类型转换"></a>Struts2 的类型转换</h1><ul>
<li>Struts2 的类型转换是基于 OGNL 表达式的。</li>
</ul>
<h2 id="类型转换的意义"><a href="#类型转换的意义" class="headerlink" title="类型转换的意义"></a>类型转换的意义</h2><ul>
<li>B/S 结构应用的请求参数是通过浏览器发送到服务器的，这些参数不可能有丰富的数据类型，因此必须在服务器端完成数据的类型转换。</li>
</ul>
<h3 id="表现层数据处理"><a href="#表现层数据处理" class="headerlink" title="表现层数据处理"></a>表现层数据处理</h3><ul>
<li>表现层数据的流向主要有两个方向：输入数据和输出数据</li>
<li>对于服务器状态呈现给客户而言，支持多种数据类型的直接输出。</li>
<li><p>对于输入数据，则需要完成由字符串类型向多种数据类型转换。程序通常无法自动完成数据类型转换，需要在代码中手动转换。</p>
</li>
<li><p>MVC 框架的另一个数据是数据校验，数据校验可分为客户端校验和服务器端校验。两者都是必不可少的，分别完成不同的过滤。</p>
</li>
</ul>
<h3 id="传统的类型转换"><a href="#传统的类型转换" class="headerlink" title="传统的类型转换"></a>传统的类型转换</h3><pre><code class="Java">String strAge = request.getParamter(&quot;age&quot;);
int age = Integer.parseInt(strAge);
</code></pre>
<h3 id="Struts2-内建的类型转换器"><a href="#Struts2-内建的类型转换器" class="headerlink" title="Struts2 内建的类型转换器"></a>Struts2 内建的类型转换器</h3><ul>
<li><p>内建了字符类型和如下类型之间的类型转换器</p>
<ul>
<li>boolean 和 Boolean</li>
<li>char Character</li>
<li>int Integer</li>
<li>long Long</li>
<li>float Float</li>
<li>double Double</li>
<li>Date 日期格式使用用户请求所在 Locale 的 SHORT 格式</li>
<li>数组 默认情况下，数组元素是字符串</li>
<li>集合 默认情况下，集合元素类型为 String，并创建一个新的 ArrayList 封装所有的字符串</li>
</ul>
</li>
<li><p>对于数组的类型转换，将按照数组元素的类型来单独转换每一个元素；但对于其他的类型转换，如果转换无法完成，系统将出现类型转换错误。</p>
</li>
<li><p>属性驱动 Action 里加上无参构造器和初始化全部属性的构造器。</p>
</li>
</ul>
<pre><code class="Java">public class RegisAction extends ActionSupport
{
    private String name;
    private String pass;
    private int age;
    private Date birth;

    public RegisAction()
    {
    }

    public RegisAction(String name, String pass, int age, Date birth)
    {
        this.name = name;
        this.pass = pass;
        this.age = age;
        this.birth = birth;
    }

    //setter and getter
}

// struts.xml 配置一样
</code></pre>
<h2 id="基于-OGNL-的类型转换"><a href="#基于-OGNL-的类型转换" class="headerlink" title="基于 OGNL 的类型转换"></a>基于 OGNL 的类型转换</h2><ul>
<li>字符串参数转换成复合类型。</li>
</ul>
<pre><code class="Java">public class RegistAction implemetns Action
{
    private User user;
    private String tip;

    // setter and getter

    public String execute() throws Exception
    {
        setTip(&quot;转换成功&quot;);
        return SUCCESS;
    }
}
</code></pre>
<pre><code class="JSP">&lt;form method=&quot;post&quot; action=&quot;regist&quot;&gt;
    &lt;input type=&quot;text&quot; name=&quot;user.name&quot; /&gt;&lt;br/&gt;
    &lt;input type=&quot;password&quot; name=&quot;user.pass&quot; /&gt;&lt;br/&gt;
    &lt;input type=&quot;text&quot; name=&quot;user.age&quot; /&gt;&lt;br/&gt;
    &lt;input type=&quot;text&quot; name=&quot;user.birth&quot; /&gt;&lt;br/&gt;
    &lt;input type=&quot;submit&quot; value=&quot;注册&quot;&gt;
&lt;/&gt;
</code></pre>
<ul>
<li><p>注意点</p>
<ul>
<li>Struts2 需要直接创建一个复合类（User 类）的实例，因此系统必须保证该复合类有无参数的构造器。</li>
<li>必须为复合类提供 setter 方法，Struts2是通过调用 Set 方法来赋值的，当然 Action 类中还有应该有包含 setUser() 方法。</li>
</ul>
</li>
</ul>
<h3 id="使用-OGNL-转换成-Map-集合"><a href="#使用-OGNL-转换成-Map-集合" class="headerlink" title="使用 OGNL 转换成 Map 集合"></a>使用 OGNL 转换成 Map 集合</h3><pre><code class="Java">public class LoginAction implements Action
{
    private Map&lt;String, User&gt; users;
    private String tip;

    // setter and getter

    @Override
    public String execute() throws Exception
    {
        if (getUsers().get(&quot;one&quot;).getName().equals(&quot;czqlan&quot;) &amp;&amp; getUsers().get(&quot;one&quot;).getPass().equals(&quot;123&quot;))
        {
            setTip(&quot;success&quot;);
            return SUCCESS;
        } else
        {
            setTip(&quot;fail&quot;);
            return ERROR;
        }
    }
}
</code></pre>
<pre><code class="JSP">&lt;form action=&quot;login.action&quot; method=&quot;post&quot;&gt;
   &lt;table align=&quot;center&quot; width=&quot;360&quot;&gt;
       &lt;caption&gt;&lt;h3&gt;封装成对象 Map&lt;/h3&gt;&lt;/caption&gt;
       &lt;tr&gt;
           &lt;td&gt;&lt;input name=&quot;users[&#39;one&#39;].name&quot;&gt;&lt;/td&gt;
       &lt;/tr&gt;
       &lt;tr&gt;
           &lt;td&gt;&lt;input name=&quot;users[&#39;one&#39;].pass&quot;&gt;&lt;/td&gt;
       &lt;/tr&gt;
       &lt;tr&gt;
           &lt;td&gt;&lt;input name=&quot;users[&#39;two&#39;].name&quot;&gt;&lt;/td&gt;
       &lt;/tr&gt;
       &lt;tr&gt;
           &lt;td&gt;&lt;input name=&quot;users[&#39;two&#39;].name&quot;&gt;&lt;/td&gt;
       &lt;/tr&gt;
       &lt;tr align=&quot;center&quot;&gt;
           &lt;td&gt;
               &lt;input type=&quot;submit&quot; value=&quot;转换&quot;&gt;
               &lt;input type=&quot;reset&quot; value=&quot;重填&quot;&gt;
           &lt;/td&gt;
       &lt;/tr&gt;
   &lt;/table&gt;
&lt;/form&gt;

// 访问 Action 的 Map 类型属性
&lt;s:property value=&quot;users[&#39;one&#39;].name&quot;/&gt;&lt;br/&gt;
&lt;s:property value=&quot;users[&#39;one&#39;].pass&quot;/&gt;&lt;br/&gt;
</code></pre>
<ul>
<li>将表单域的 name 属性设置为 “Action 属性名[‘key 值’].属性名”</li>
</ul>
<h3 id="使用-OGNL-转换成-List-集合"><a href="#使用-OGNL-转换成-List-集合" class="headerlink" title="使用 OGNL 转换成 List 集合"></a>使用 OGNL 转换成 List 集合</h3><pre><code class="JSP">// List&lt;User&gt;

&lt;input type=&quot;text&quot; name=&quot;user[0].name&quot;/&gt;
&lt;input type=&quot;text&quot; name=&quot;user[0].pass&quot;/&gt;
&lt;input type=&quot;text&quot; name=&quot;user[1].name&quot;/&gt;
&lt;input type=&quot;text&quot; name=&quot;user[1].pass&quot;/&gt;

&lt;s:property value=&quot;users[0].name&quot;/&gt;
&lt;s:property value=&quot;users[0].pass&quot;/&gt;
</code></pre>
<h2 id="自定义类型转换器"><a href="#自定义类型转换器" class="headerlink" title="自定义类型转换器"></a>自定义类型转换器</h2><ul>
<li>一个字符串转换成一个符合对象。（<code>abc,xyz</code>转换成 一个 User 类型实例）</li>
</ul>
<h3 id="系统需求"><a href="#系统需求" class="headerlink" title="系统需求"></a>系统需求</h3><ul>
<li>用户信息的用户名和密码以英文逗号隔开</li>
</ul>
<h3 id="实现类型转换器"><a href="#实现类型转换器" class="headerlink" title="实现类型转换器"></a>实现类型转换器</h3><ul>
<li>TypeConverter 接口</li>
</ul>
<pre><code class="Java">public interface TypeConverter
{
    public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType);
}
</code></pre>
<ul>
<li>OGNL 项目提供了一个该接口的实现类：DefaultTypeConverter</li>
<li>实现自定义类型转换器需要重写 DefaultTypeConverter 类的 convertValue 方法。</li>
</ul>
<pre><code class="Java">public class UserConverter extends DefaultTypeConverter
{
    @Override
    public Object convertValue(Map context, Object value, Class toType)
    {
        if (toType == User.class)
        {
            String[] params = (String[]) value;
            User user = new User();
            String[] userValues = params[0].split(&quot;,&quot;);
            user.setName(userValues[0]);
            user.setPass(userValues[1]);
            return user;
        } else if (toType == String.class)
        {
            User user = (User) value;
            return &quot;&lt;&quot; + user.getName() + &quot;,&quot; + user.getPass() + &quot;&gt;&quot;;
        }
        return null;
    }
}

// 双向转换
// context 是类型转换环境的上下文
// value 是需要转换的参数
// toType 是需要转换的目标类型
</code></pre>
<ul>
<li>把所有请求都视为字符串数组。</li>
<li><code>DefaultTypeConverter</code> 是通过 HttpServletRequest 的 getParameterValues(name) 方法来获取请求参数值的。因此它获取的请求参数总是字符串数组。</li>
</ul>
<h3 id="局部类型转换器"><a href="#局部类型转换器" class="headerlink" title="局部类型转换器"></a>局部类型转换器</h3><ul>
<li><p>类型转换器的注册方式</p>
<ul>
<li>注册局部类型转换器：局部类型转换器仅仅对某个 Action 的属性起作用。</li>
<li>注册全局类型转换器：全局类型转换器对所有 Action 的特定类型的属性都会生效。</li>
<li>使用 JDK 1.5 的 Annotation 来注册类型转换器</li>
</ul>
</li>
</ul>
<pre><code class="properties">// 同目录下 ActionName-conversion.properties
// propertyName=类型转换器

// LoginAction-conversion.properties
user=org.action.UserConverter
// 当浏览者提交请求时，请求中的 user 请求参数将会先被该类型转换器处理
</code></pre>
<ul>
<li>局部转换器只对指定 Action 的制定属性生效，全局类型转换器对指定类型的全部属性起作用。</li>
</ul>
<h3 id="全局类型转换器"><a href="#全局类型转换器" class="headerlink" title="全局类型转换器"></a>全局类型转换器</h3><pre><code class="properties">// xwork-conversion.properties
// 复合类型=对应的类型转换器

org.domain.User=org.action.UserConverter
</code></pre>
<h3 id="局部类型转换器和全局类型转换器的说明"><a href="#局部类型转换器和全局类型转换器的说明" class="headerlink" title="局部类型转换器和全局类型转换器的说明"></a>局部类型转换器和全局类型转换器的说明</h3><ul>
<li>局部类型转换器对指定 Action 的指定属性起作用，一个属性只调用 convertValue 方法一次。<code>List&lt;User&gt;</code></li>
<li><p>全局类型转换器对所有 Action 的特定类型起作用，因此可能对一个属性多次调用 convertValue 方法进行转换。</p>
<ul>
<li>当该属性是一个数组或集合时，该数组或集合中包含几个该类型的元素，那就会调用几次 convertValue 方法。</li>
</ul>
</li>
</ul>
<h3 id="基于-Struts2-的类型转换器"><a href="#基于-Struts2-的类型转换器" class="headerlink" title="基于 Struts2 的类型转换器"></a>基于 Struts2 的类型转换器</h3><ul>
<li>Struts 2 提供了一个 Struts2TypeConverter 抽象类。</li>
</ul>
<pre><code class="Java">public abstract class StrutsTypeConverter extends DefaultTypeConverter 
{
    @Override
    public Object convertValue(Map context, Object o, Class toClass)
    {
        if (toClass.equals(String.class))
        {
            return convertToString(context, o);
        } else if (o instanceof String[])
        {
            return convertFromString(context, (String[]) o, toClass);
        } else if (o instanceof String)
        {
            return performFallbackConversion(context, new String[]{(String) o}, toClass);
        } else
        {
            return performFallbackConversion(context, o, toClass);
        }
    }

    protected Object performFallbackConversion(Map context, Object o, Class toClass) {
        return super.convertValue(context, o, toClass);
    }

    public abstract Object convertFromString(Map content, String[] values, Class toClass);

    public abstract String convertToString(Map context, Object o);
}

// 将两个不同的转换方向替换成不同方法。
</code></pre>
<ul>
<li>实现两个方法。</li>
</ul>
<pre><code class="Java">public class UserDemoConverter extends StrutsTypeConverter
{
    @Override
    public Object convertFromString(Map context, String[] values, Class toClass)
    {
        User user = new User();
        String[] userValues = values[0].split(&quot;,&quot;);
        user.setName(userValues[0]);
        user.setPass(userValues[1]);
        return user;
    }

    @Override
    public String convertToString(Map map, Object o)
    {
        User user = (User) o;
        return &quot;&lt;&quot; + user.getName() + &quot;,&quot; + user.getPass() + &quot;&gt;&quot;;
    }
}
</code></pre>
<h3 id="数组属性的类型转换器"><a href="#数组属性的类型转换器" class="headerlink" title="数组属性的类型转换器"></a>数组属性的类型转换器</h3><ul>
<li><code>User[]</code>，多个 user 需要遍历</li>
</ul>
<h3 id="集合属性的类型转换器"><a href="#集合属性的类型转换器" class="headerlink" title="集合属性的类型转换器"></a>集合属性的类型转换器</h3><ul>
<li><code>List&lt;User&gt;</code></li>
</ul>
<h2 id="集合类型转换的高级特性"><a href="#集合类型转换的高级特性" class="headerlink" title="集合类型转换的高级特性"></a>集合类型转换的高级特性</h2><ul>
<li><code>List&lt;User&gt;</code> 不使用泛型。</li>
</ul>
<h3 id="指定集合元素的类型"><a href="#指定集合元素的类型" class="headerlink" title="指定集合元素的类型"></a>指定集合元素的类型</h3><ul>
<li>使用 Struts2 的配置文件：在局部类型转换配置文件中指定集合元素的数据类型。</li>
</ul>
<pre><code class="properties">// Element_xxx=集合元素类型
Element_users=org.domain.User

// Map
// Key_xxx=key元素类型
// Element_xxx=value元素类型
</code></pre>
<h3 id="为-Set-集合的元素指定索引属性"><a href="#为-Set-集合的元素指定索引属性" class="headerlink" title="为 Set 集合的元素指定索引属性"></a>为 Set 集合的元素指定索引属性</h3><ul>
<li><p>对 Set 类型的属性则无法通过索引访问集合元素。</p>
</li>
<li><p>访问 Set 元素用的是 圆括号，而不是方括号。但对于数组，List 和 Map 属性，通过方括号来访问指定的集合元素。</p>
</li>
</ul>
<h2 id="类型转换中的错误处理"><a href="#类型转换中的错误处理" class="headerlink" title="类型转换中的错误处理"></a>类型转换中的错误处理</h2><ul>
<li>表现层数据涉及两个处理：数据校验和类型转换是紧密相关的。</li>
</ul>
<h3 id="类型转换的错误处理流程"><a href="#类型转换的错误处理流程" class="headerlink" title="类型转换的错误处理流程"></a>类型转换的错误处理流程</h3><ul>
<li>Struts2 提供了一个名为 conversionError 的拦截器</li>
<li><p>如果 Struts2 的类型转换器执行类型转换时出现错误，该拦截器负责将对应错误封装成表单域错误（fieldError），并将错误信息放入 ActionContext 中。</p>
</li>
<li><p>conversionError 拦截器实际上是一个 Throws 处理。</p>
</li>
<li><p>当 conversionError 拦截器对转换异常进行处理后，系统会跳转到名为 input 的逻辑视图。</p>
</li>
<li><p>为了让 Struts2 框架处理类型转换错误，以及使用后面的数据校验机制，系统的 Action 类都应该通过继承 ActionSupport 类来实现。</p>
</li>
<li>ActionSupport 类为完成类型转换错误处理，数据校验实现了许多基础工作。</li>
</ul>
<h3 id="处理类型转换错误"><a href="#处理类型转换错误" class="headerlink" title="处理类型转换错误"></a>处理类型转换错误</h3><h3 id="输出类型转换错误"><a href="#输出类型转换错误" class="headerlink" title="输出类型转换错误"></a>输出类型转换错误</h3><pre><code class="JSP">&lt;s:fielderror/&gt;
// Invalid filed value for fileld &quot;user&quot;
// user ，Action 中的属性名
</code></pre>
<ul>
<li>如果使用 Struts2 的表单标签来生成表单，并使用 xhtml 主题，Struts2 的表单标签将会自动输出错误提示。</li>
</ul>
<pre><code class="XML">xwork.default.invalid.fieldvalue={0}字段类型转换失败！

// struts.xml
&lt;constant name=&quot;struts.custom.il8n.resources&quot; value=&quot;globalMessages&quot;/&gt;
</code></pre>
<ul>
<li>对特定字段指定特别的提示信息</li>
</ul>
<pre><code class="properties">// ActionName.properties
// invalid.fieldvalue.属性名=提示信息

// LoginAction.properties 与 Action 相同位置
invalid.fieldvalue.birth=生日信息必须满足 yyyy-MM-dd 格式
</code></pre>
<h3 id="处理集合属性的转换错误"><a href="#处理集合属性的转换错误" class="headerlink" title="处理集合属性的转换错误"></a>处理集合属性的转换错误</h3><ul>
<li>分别显示错误</li>
</ul>
<pre><code class="JSP">&lt;s:iterator value=&quot;new int[3]&quot; status=&quot;stat&quot;&gt;
   &lt;tr&gt;
       &lt;td&gt;用户&lt;s:property value=&quot;%{#stat.index}&quot;/&gt;&lt;/td&gt;
       &lt;input name=&quot;users[&lt;s:property value=&quot;%{#stat.index}&quot;/&gt;&quot; type=&quot;text&quot;&gt;
   &lt;/tr&gt;
&lt;/s:iterator&gt;
</code></pre>
<h1 id="Struts2-的输入校验"><a href="#Struts2-的输入校验" class="headerlink" title="Struts2 的输入校验"></a>Struts2 的输入校验</h1><ul>
<li><p>输入校验分为客户端校验和服务器校验</p>
<ul>
<li>客户端校验主要是过滤正常用户的误操作，主要通过 JavaScript 代码完成</li>
<li>服务端校验是整个应用阻止非法数据的最后防线，主要通过在应用中编程实现。</li>
</ul>
</li>
</ul>
<h2 id="输入校验概述"><a href="#输入校验概述" class="headerlink" title="输入校验概述"></a>输入校验概述</h2><h3 id="为什么需要输入校验"><a href="#为什么需要输入校验" class="headerlink" title="为什么需要输入校验"></a>为什么需要输入校验</h3><ul>
<li>在 <code>《Writing Secure Code》</code> 一书中名言：All Input Is Evil</li>
</ul>
<h3 id="客户端校验"><a href="#客户端校验" class="headerlink" title="客户端校验"></a>客户端校验</h3><ul>
<li>客户端校验就是通过 JavaScript 在数据收集页面（通常是表单输入页）中进行初步过滤。</li>
<li>使用第三方校验库 Valiadation.js 库</li>
</ul>
<h3 id="服务器端校验"><a href="#服务器端校验" class="headerlink" title="服务器端校验"></a>服务器端校验</h3><ul>
<li>很多恶意的 Cracker ，并不是通过浏览器来 crack 某个应用，他会采用更低层的 Socket 通信进行 crack</li>
<li><p>或者，将网页源代码保存到本机，修改源代码取消表单元素的输入校验绑定，并修改该表单的 action 属性。</p>
</li>
<li><p>客户端校验把这些误输入阻止在客户端，从而降低了服务器的负载。服务器端校验是请求数据进入系统之前的最后屏障。</p>
</li>
</ul>
<h3 id="类型转换和输入校验"><a href="#类型转换和输入校验" class="headerlink" title="类型转换和输入校验"></a>类型转换和输入校验</h3><ul>
<li>类型转换在输入校验之前进行。</li>
<li>有时类型转换和输入校验同时完成。进行类型转换时可以完成基本的输入校验，成功转换成有效数据类型只是成为有效数据的必要条件，在类型转换的基础上，还要进行额外的输入校验。</li>
</ul>
<h2 id="基本输入校验"><a href="#基本输入校验" class="headerlink" title="基本输入校验"></a>基本输入校验</h2><h3 id="编写校验规则文件"><a href="#编写校验规则文件" class="headerlink" title="编写校验规则文件"></a>编写校验规则文件</h3><ul>
<li>采用 Struts2 的校验框架时，只需要为该 Action 指定一个校验文件即可。</li>
</ul>
<pre><code class="XML">// RegistAction-validation.xml
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE validators PUBLIC &quot;-//Apache Struts//XWork Validator 1.0.3//EN&quot;
        &quot;http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd&quot;&gt;

&lt;validators&gt;
    &lt;field name=&quot;name&quot;&gt;
        &lt;field-validator type=&quot;requiredstring&quot;&gt;
            &lt;param name=&quot;trim&quot;&gt;true&lt;/param&gt;
            &lt;message key=&quot;user.required&quot;/&gt;
        &lt;/field-validator&gt;
        &lt;field-validator type=&quot;regex&quot;&gt;
            &lt;param name=&quot;regexExpression&quot;&gt;&lt;![CDATA[
            (\w{4,25})
            ]]&gt;&lt;/param&gt;
            &lt;message&gt;您输入的用户名只能是字母和数字，且长度必须在 4 到 25 之间&lt;/message&gt;
        &lt;/field-validator&gt;
    &lt;/field&gt;
    &lt;field name=&quot;pass&quot;&gt;
        &lt;field-validator type=&quot;requiredstring&quot;&gt;
            &lt;param name=&quot;trim&quot;&gt;true&lt;/param&gt;
            &lt;message key=&quot;pass.required&quot;/&gt;
        &lt;/field-validator&gt;
        &lt;field-validator type=&quot;regex&quot;&gt;
            &lt;param name=&quot;regexExpression&quot;&gt;&lt;![CDATA[
            (\w{4,25})
            ]]&gt;&lt;/param&gt;
            &lt;message&gt;您输入的密码只能是字母和数字，且长度必须在 4 到 25 之间&lt;/message&gt;
        &lt;/field-validator&gt;
    &lt;/field&gt;
    &lt;field name=&quot;age&quot;&gt;
        &lt;field-validator type=&quot;int&quot;&gt;
            &lt;param name=&quot;min&quot;&gt;1&lt;/param&gt;
            &lt;param name=&quot;max&quot;&gt;150&lt;/param&gt;
            &lt;message&gt;年龄必须在 1 到 150 之间&lt;/message&gt;
        &lt;/field-validator&gt;
    &lt;/field&gt;
    &lt;field name=&quot;birth&quot;&gt;
        &lt;field-validator type=&quot;date&quot;&gt;
            &lt;param name=&quot;min&quot;&gt;1900-01-01&lt;/param&gt;
            &lt;param name=&quot;max&quot;&gt;2050-02-21&lt;/param&gt;
            &lt;message&gt;生日必须在${min}到${max}之间&lt;/message&gt;
        &lt;/field-validator&gt;
    &lt;/field&gt;
&lt;/validators&gt;
</code></pre>
<ul>
<li>Struts2 的校验文件规则与 Struts1 的校验文件设计方式不同，Struts2 中的每个 Action 都有一个校验文件。Struts2 的校验框架可以更方便地进行模块化开发。</li>
<li><p>Struts2的 Action 与校验规则文件具有如下共性</p>
<ul>
<li>Action 类与校验规则文件保存在同一路径下。</li>
<li>Action 类的类名作为校验规则文件的文件名前缀。<code>&lt;Action名字&gt;-validate.xml</code></li>
</ul>
</li>
<li><p>增加了校验文件后，系统会自动加载该文件。当用户提交请求时，Struts2 的校验框架会根据该文件对用户请求进行校验。</p>
</li>
<li>校验失败后，Struts2 将会自动返回名为 input 的逻辑视图。</li>
</ul>
<pre><code class="XML">// 国际化 RegistAction.properties
name.required=..
name.regex=...
pass.required=..
pass.regex=...
age.regex=..
birth.range=..
</code></pre>
<h3 id="使用客户端校验"><a href="#使用客户端校验" class="headerlink" title="使用客户端校验"></a>使用客户端校验</h3><ul>
<li>增加客户端校验，将输入页面的表单元素改为使用 Struts2 标签来生成表单，并为该表单增加 <code>validate=&quot;true&quot;</code> 属性即可。</li>
</ul>
<pre><code class="JSP">// registForm.jsp
&lt;s:form action=&quot;regist&quot; validate=&quot;true&quot;&gt;
    &lt;s:textfield label=&quot;用户名&quot; name=&quot;name&quot;/&gt;
    &lt;s:password label=&quot;密码&quot; name=&quot;pass&quot;/&gt;
    &lt;s:textfield label=&quot;年龄&quot; name=&quot;age&quot;/&gt;
    &lt;s:textfield label=&quot;生日&quot; name=&quot;birth&quot;/&gt;
    &lt;s:submit value=&quot;提交&quot;/&gt;
&lt;/s:form&gt;
</code></pre>
<pre><code class="XML">// RegistAction-validation.xml
&lt;field name=&quot;name&quot;&gt;
   &lt;field-validator type=&quot;requiredstring&quot;&gt;
       &lt;param name=&quot;trim&quot;&gt;true&lt;/param&gt;
       &lt;message&gt;${getText(&quot;name.required&quot;)}&lt;/message&gt;
   &lt;/field-validator&gt;
   &lt;field-validator type=&quot;regex&quot;&gt;
       &lt;param name=&quot;regexExpression&quot;&gt;&lt;![CDATA[
       (\w{4,25})
       ]]&gt;&lt;/param&gt;
       &lt;message&gt;${getText(&quot;name.regex&quot;)}&lt;/message&gt;
   &lt;/field-validator&gt;
&lt;/field&gt;
</code></pre>
<ul>
<li>客户端校验是基于 JavaScript 完成的，由于 JavaScript 脚本本身的限制，有些服务器端校验不能转换成客户端校验。</li>
<li><p>客户端校验支持几种校验器</p>
<ul>
<li>require validator 必填校验器</li>
<li>requirestring validator 必填字符串校验器</li>
<li>stringlength validator 字符串长度校验器</li>
<li>regex validator 正则表达式校验器</li>
<li>email validaror 邮件校验器</li>
<li>url validator 网址校验器</li>
<li>int validator 整数校验器</li>
<li>double validator 双精度数校验器</li>
</ul>
</li>
<li><p>客户端校验注意点</p>
<ul>
<li>Struts2 的 <s:form...> 元素有一个 theme 属性，不要将该属性指定为 simple</s:form...></li>
<li>不要在校验规则文件的错误提示信息中，直接食用 key 来指定国际化信息。</li>
</ul>
</li>
</ul>
<h2 id="校验器的配置风格"><a href="#校验器的配置风格" class="headerlink" title="校验器的配置风格"></a>校验器的配置风格</h2><ul>
<li><p>Struts2 提供了两种方式来配置校验规则 </p>
<ul>
<li>字段校验器风格 字段优先</li>
<li>非字段校验器风格 校验器优先</li>
</ul>
</li>
</ul>
<h3 id="字段校验器"><a href="#字段校验器" class="headerlink" title="字段校验器"></a>字段校验器</h3><pre><code class="DTD">// 在 validators 元素的 field 或 validator 中都可以出现一次或无限次。
&lt;!ELEMENT validators (field|validator)+&gt;

// validators 是校验规则文件的根元素，该根元素下可以出现两个元素
// &lt;field../&gt;
// &lt;validator../&gt;

// 出现第一种就是 字段优先，就是字段校验器配置风格
// 出现第二种元素，就是校验器优先，就是非字段校验器风格
</code></pre>
<ul>
<li>形式</li>
</ul>
<pre><code class="XML">&lt;field name=&quot;被校验的字段&quot;&gt;
   &lt;field-validator type=&quot;校验器名&quot;&gt;
       &lt;param name=&quot;参数名&quot;&gt;参数值&lt;/param&gt;
       &lt;message key=&quot;I18Nkey&quot;&gt;校验失败后的提示信息&lt;/&gt;
   &lt;/field-validator&gt;
   ...
&lt;/field&gt;
...

// 每个 field-validator 都必须有 &lt;message.../&gt; 元素
</code></pre>
<h3 id="非字段校验器配置风格"><a href="#非字段校验器配置风格" class="headerlink" title="非字段校验器配置风格"></a>非字段校验器配置风格</h3><pre><code class="XML">&lt;validator type=&quot;校验器名&quot;&gt;
    &lt;param name=&quot;fieldName&quot;&gt;需要被校验的字段&lt;/param&gt;
    ...
    &lt;param name=&quot;参数名&quot;&gt;参数值&lt;/param&gt;
    ...

    &lt;message key=&quot;I18Nkey&quot;&gt;校验失败后的提示信息&lt;/message&gt;
&lt;/validator&gt;
...
</code></pre>
<h3 id="短路校验器"><a href="#短路校验器" class="headerlink" title="短路校验器"></a>短路校验器</h3><ul>
<li></li>
</ul>

 
                <!-- Meta -->
                <div class="post-meta">
                    <hr>
                    <br>
                    <div class="post-tags">
                        
                            

<a href="/tags/Struts2/">#Struts2</a>


                        
                    </div>
                    <div class="post-date">
                        2017 年 06 月 26 日
                    </div>
                </div>
            </div>

            <!-- Comments -->
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <!-- Disqus Comments -->


            </div>
        </div>
    </div>
</article>
</section>

    <!-- Scripts -->
    <!-- jQuery -->
<script src="//cdn.bootcss.com/jquery/2.2.1/jquery.min.js"></script>
<!-- Bootstrap -->
<script src="//cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

<script type="text/javascript">
	hljs.initHighlightingOnLoad();
	console.log('Hexo-theme-hollow designed by zchen9 http://www.chen9.info © 2015-' + (new Date()).getFullYear());
</script>

    <!-- Google Analytics -->
    

</body>

</html>